﻿using KeepAnimals.DAL._DataAccess;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using KeepAnimals.Library;

namespace KeepAnimals.DAL
{
    /// <summary>
    /// Classe de base pour tous les objets "mappés",
    /// c.a.d décorés avec un attribut "[Table("..."]"
    /// </summary>
    public abstract class DataMapper
    {
        private int _id;

        private Dictionary<string, MappedProperty> _mappedProperties;
        private AbstractDbAccessor _dbAccessor;

        /// <summary>
        /// Valeur affectée à l'Id quand l'objet en cours
        /// ne provient pas de la BDD..
        /// </summary>
        private const int UNDEFINED_ID = -1;


        /// <summary>
        /// Ctor
        /// Affecte -1 à la propriété "Id" afin de bien 
        /// définir que l'objet décoré ne provient pas de 
        /// la BDD.
        /// </summary>
        protected DataMapper()
            : this(UNDEFINED_ID)
        { }

        /// <summary>
        /// Ctor
        /// NB :    Ce constructeur est celui qui sera toujours appelé
        ///         quel que soit le cas.
        /// </summary>
        /// <param name="id">The identifier.</param>
        protected DataMapper(int id)
        {
            _id = id;

            // Initialise le dictionnaire de MappedProperty nommé
            // "MappedPropertie"..
            initMappedProperties();

            // On initialise le bon type de DbAccessor (SqlServer ou MySql)
            // en appelant notre factory, qui va aller chercher la valeur 
            // dans le fichier de config de l'application ("app.config").
            _dbAccessor = DbAccessorFactory.Create(this);

        }

        /// <summary>
        /// Initializes the mapped properties.
        /// </summary>
        private void initMappedProperties()
        {
            // On instancie le dictionnaire..
            _mappedProperties = new Dictionary<string, MappedProperty>();

            // On obtient le type de l'objet à traiter..
            var type = this.GetType();

            // Pour chacune de ses propriétés publiques..
            foreach (var property in type.GetProperties())
            {
                // Si la propriété est bien décorée avec un FieldAttribute..
                if (property.IsDecoratedWith<FieldAttribute>())
                {
                    // On construit un nouvel objet MappedProperty..
                    var mappedProp = new MappedProperty(this, property);

                    // et on l'ajoute au dictionnaire de MappedProperty
                    // nommé "MappedProperties"
                    _mappedProperties.Add(property.Name, mappedProp);
                }
            }
        }

        /// <summary>
        /// Gets or sets the identifier.
        /// </summary>
        [Field("Id", IsPrimarykey = true)]
        public int Id
        {
            get { return _id; }
            protected set { _id = value; }
        }

        /// <summary>
        /// Gets the mapped properties.
        /// </summary>
        public Dictionary<string, MappedProperty> MappedProperties
        {
            get { return _mappedProperties; }
        }

        /// <summary>
        /// Gets or sets the database accessor.
        /// </summary>
        public AbstractDbAccessor DbAccessor
        {
            get { return _dbAccessor; }
            protected set { _dbAccessor = value; }
        }

        /// <summary>
        /// Gets the name of the mapped table.
        /// </summary>
        /// <returns>the name of the mapped table.</returns>
        public string GetMappedTableName()
        {
            var tableAttribute = this.GetType().GetUniqueAttribute<TableAttribute>();

            return tableAttribute.TableName;
        }
    }
}